package com.example.base.springboot;


import com.example.base.app.App;
import com.example.base.app.AppHandlerLoad;
import com.example.base.app.AppInfo;
import com.example.base.app.AppSecretInfo;
import com.example.base.job.LoadJobService;
import com.example.base.plugin.LoadPluginService;
import com.example.base.system.SysAppService;
import com.example.base.system.domain.SysApp;
import com.example.base.util.*;
import com.example.base.utils.ReflectUtils;
import com.example.base.websocket.plugin.AppWebSocketPlugin;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;


@Slf4j
@Component
public class DynamicLoad {

    private Map<String,String> BeanNameMap=new HashMap<>();

    private static Logger logger = LoggerFactory.getLogger(DynamicLoad.class);

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private SysAppService sysAppService;

    @Autowired
    private LoadPluginService loadPluginService;

    @Autowired
    private AppWebSocketPlugin appWebSocketPlugin;

    public void unrefApp(SysApp sysApp) throws Exception {
        if(sysApp != null ){
            SysApp app=sysAppService.getSysAppByAppName(sysApp.getAppName());
            URLClassLoader urlClassLoader=URLClassLoaderUtils.getUrlClassLoader(app.getAppPath());
            AppInfo appInfo=app.getAppInfo();
            if(urlClassLoader instanceof AppClassLoader){
                AppClassLoader appClassLoader=(AppClassLoader) urlClassLoader;
                Map<String, Class<?>> stringClassMap=appClassLoader.getLoadedClasses();
                for(Class<?> cls:stringClassMap.values()){
                    RestController controller=SpringAnnotationUtils.hasRestController(cls);
                    if(controller!=null){
                        unregisterController(appInfo,cls);
                    }
                }
            }
        }
    }

    @Autowired
    private LoadJobService loadJobService;

    @Autowired
    private AppHandlerLoad appHandlerLoad;


    public void loadJar(File file){
        if(file.getName().endsWith(".jar")){
            try {
                File newFile=new File(file.getPath());
                log.info("app:{} 应用开始加载了",file.getName());
                sysAppService.sysAppStrat(newFile.getName());
                Thread.sleep(5000);
                while (!newFile.exists()){
                    newFile=new File(file.getPath());
                    log.info("文件{}不存在，继续开始加载",file.getPath());
                }
                Set<Class<?>> cls= LoadJarUtils.loadAllJarFromAbsolute(newFile.getPath());
                AppInfo appInfo=null;
                for(Class<?> s:cls){
                    Boolean b= AppUtils.isAppInfo(s);
                    if(b){
                        appInfo= (AppInfo) ReflectUtils.createObjectByClass(s);
                        break;
                    }
                }
                if(appInfo==null){
                    log.info("app {} 加载取消,app未非法应用",file.getName());
                    return;
                }
                if(appInfo.getAppSecret()==null){
                    log.info("app:{}秘钥为空, 已停止加载 ",file.getName());
                }
                AppSecretInfo appSecretInfo =EncryptionTool.getSecretInfo(appInfo.getAppSecret());
                if(appSecretInfo==null){
                    log.info("非法秘钥: {}",appInfo.getAppSecret());
                    log.info("app {} 秘钥非法,已停止加载app",file.getName());
                    return;
                }
                if(appSecretInfo.getExpireSecond() < System.currentTimeMillis()){
                    log.info("应用授权日期: {} 已到期,请更新授权",appSecretInfo.getExpireDate());
                    return;
                }
                if(appSecretInfo.getAppVersion()==null){
                    log.info("app版本为空, 本次加载暂停");
                    return;
                }
                if(appSecretInfo.getAppName()==null){
                    log.info("app应用名称为空, 本次加载暂停");
                    return;
                }
                if(!appSecretInfo.getAppVersion().equals(appInfo.getAppVersion())){
                    log.info("app版本与秘钥提供版本不一致,本次加载暂停");
                    return;
                }
                if(!appSecretInfo.getAppName().equals(appInfo.getAppName())){
                    log.info("app名称与秘钥提供app名称不一致,本次加载暂停");
                    return;
                }
                log.info("应用到期日期:{}",appSecretInfo.getExpireDate());
                log.info("应用版本:{}",appSecretInfo.getAppVersion());
                log.info(appSecretInfo.getAppName()+" 开始加载了");
                for(Class<?> s:cls){
                    Service service= SpringAnnotationUtils.hasService(s);
                    if(service!=null){
                        loadService(s);
                    }
                }
                for (Class<?> s:cls){
                    RestController controller=SpringAnnotationUtils.hasRestController(s);
                    if(controller!=null){
                        unregisterController(appInfo,s);
                        registerController(s,appInfo);
                    }
                }
                App app=new App(appInfo,appSecretInfo,file.getPath());
                com.example.base.app.AppUtils.add(appSecretInfo.getAppName(),app);
                sysAppService.operateSysApp(file,appInfo,appSecretInfo);
                appHandlerLoad.load(app.getAppInfo().getAppName());
                loadJobService.loadJob(app.getAppInfo().getAppName());
                loadPluginService.loadPlugin(app.getAppInfo().getAppName());
                appWebSocketPlugin.loadAppWebSocketPlugin(app.getAppInfo().getAppName());
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }



    private void loadMapper(Class<?> clazz){
        String beanName = clazz.getName();
        loadBean(clazz);
        beanName = StringUtils.uncapitalize(beanName);
        SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);
        if(!sqlSessionFactory.getConfiguration().getTypeAliasRegistry().getTypeAliases().containsKey(beanName.replace("/", "."))){
            sqlSessionFactory.getConfiguration().getTypeAliasRegistry().registerAlias(beanName.replace("/", "."), clazz);
        }
    }

    private void loadService(Class<?> clazz){
        try {
            Class<?>[] interfaces = clazz.getInterfaces();
            if (interfaces.length == 0) {
                return;
            }
            String beanName=clazz.getName();

            DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ((ConfigurableApplicationContext)
                    applicationContext).getBeanFactory();

            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getRawBeanDefinition();
            //设置当前bean定义对象是单利的
            beanDefinition.setScope("singleton");
            // 将变量首字母置小写
            beanName = StringUtils.uncapitalize(beanName);
            //判断bean是否被注册
            if (beanFactory.containsBeanDefinition(beanName)) {
                beanFactory.removeBeanDefinition(beanName);
            }
            // 将构建的BeanDefinition交由Spring管理
            beanFactory.registerBeanDefinition(beanName, beanDefinition);
            // 手动构建实例，并注入base service 防止卸载之后不再生成
          //  Object obj = clazz.newInstance();
           // beanFactory.registerSingleton(beanName, obj);

            beanFactory.autowireBean(clazz);
            beanFactory.initializeBean(clazz, beanName);

            log.info("beanName {} class:{} 加载成功",beanName,clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void loadBean(Class<?> clazz){
        try {
            String beanName = clazz.getName();
            DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ((ConfigurableApplicationContext)
                    applicationContext).getBeanFactory();

            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getRawBeanDefinition();
            //设置当前bean定义对象是单利的
            beanDefinition.setScope("singleton");

            // 将变量首字母置小写
            beanName = StringUtils.uncapitalize(beanName);
            //判断bean是否被注册
            if (beanFactory.containsBeanDefinition(beanName)) {
                beanFactory.removeBeanDefinition(beanName);
            }
            // 将构建的BeanDefinition交由Spring管理
            beanFactory.registerBeanDefinition(beanName, beanDefinition);
            // 手动构建实例，并注入base service 防止卸载之后不再生成
          //  Object obj = clazz.newInstance();
            //beanFactory.registerSingleton(beanName, obj);

            beanFactory.autowireBean(clazz);
            beanFactory.initializeBean(clazz, beanName);


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void loadRequestMappring(Class<?> clazz) throws NoSuchMethodException {
        try {
            String beanName = clazz.getName();
            DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ((ConfigurableApplicationContext)
                    applicationContext).getBeanFactory();


            // 定义BeanDefinition
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getRawBeanDefinition();
            //设置当前bean定义对象是单利的
            beanDefinition.setScope("singleton");
            // 将变量首字母置小写
            beanName = StringUtils.uncapitalize(beanName);

            //判断bean是否被注册
            if (beanFactory.containsBeanDefinition(beanName)) {
                beanFactory.removeBeanDefinition(beanName);
            }
            // 将构建的BeanDefinition交由Spring管理
            beanFactory.registerBeanDefinition(beanName, beanDefinition);
            // 手动构建实例，并注入base service 防止卸载之后不再生成
          //  Object obj = clazz.newInstance();
           // beanFactory.registerSingleton(beanName, obj);

            beanFactory.autowireBean(clazz);
            beanFactory.initializeBean(clazz, beanName);

            final RequestMappingHandlerMapping requestMappingHandlerMapping =
                    applicationContext.getBean(RequestMappingHandlerMapping.class);

            String handler = beanName;
            Object controller = null;
            try {
                controller = applicationContext.getBean(handler);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (controller == null) {
                return;
            }
            Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
            RequestMappingInfo existingHandler=findExistingHandler(handlerMethods,handler);
            if(existingHandler!=null){
                requestMappingHandlerMapping.unregisterMapping(existingHandler);
            }
            // 注册Controller
            Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().
                    getDeclaredMethod("detectHandlerMethods", Object.class);
            // 将private改为可使用
            method.setAccessible(true);
           // method.invoke(requestMappingHandlerMapping, handler);
        } catch (Exception e){
            e.printStackTrace();
        }

    }

    private RequestMappingInfo findExistingHandler(Map<RequestMappingInfo, HandlerMethod> handlerMethods, Object handler) {
        for (Map.Entry<RequestMappingInfo,HandlerMethod> methodEntry:handlerMethods.entrySet()){
            if(methodEntry.getValue().getBean().equals(handler)){
                return methodEntry.getKey();
            }
        }
        return null;
    }


    /**
     * 注册controller
     * @param cls
     * @throws Exception
     */
    private void registerController(Class<?> cls,AppInfo appInfo) throws Exception {

        //先注册bean
        registerBean(cls,appInfo);

        final RequestMappingHandlerMapping requestMappingHandlerMapping =
                applicationContext.getBean("requestMappingHandlerMapping",RequestMappingHandlerMapping.class);


        if (requestMappingHandlerMapping != null) {
            String handler = StringUtils.uncapitalize(cls.getName());
            Object controller = applicationContext.getBean(handler);
            if (controller == null) {
                return;
            }
            addInterceptor(appInfo,cls);
            //注册Controller
            Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().
                    getDeclaredMethod("detectHandlerMethods", Object.class);
            //将private改为可使用
            method.setAccessible(true);
            method.invoke(requestMappingHandlerMapping, handler);

            final Class<?> targetClass = cls;
            ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
                public void doWith(Method method) {
                    Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                    try {
                        Method createMappingMethod = RequestMappingHandlerMapping.class.getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
                        createMappingMethod.setAccessible(true);
                        RequestMappingInfo requestMappingInfo = (RequestMappingInfo) createMappingMethod.invoke(requestMappingHandlerMapping, specificMethod, targetClass);

                        if (requestMappingInfo != null) {
                            RequestMappingInfo newRequestMappingInfo = manipulateRequestMappingInfo(appInfo,requestMappingInfo);

                            // 注销原有映射
                            requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);

                            // 注册新映射
                            requestMappingHandlerMapping.registerMapping(newRequestMappingInfo, controller, specificMethod);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, ReflectionUtils.USER_DECLARED_METHODS);
        }
    }


    private RequestMappingInfo manipulateRequestMappingInfo(AppInfo appInfo,RequestMappingInfo requestMappingInfo) {
        PatternsRequestCondition patternsCondition = requestMappingInfo.getPatternsCondition();
        Set<String> patterns = patternsCondition.getPatterns();
        Set<String> newPatterns = new LinkedHashSet<>();
        String prefix="/app/"+appInfo.getAppName();
        patterns.forEach(pattern -> newPatterns.add(prefix + pattern));
        PatternsRequestCondition newPatternsCondition = new PatternsRequestCondition(newPatterns.toArray(new String[0]));

        return new RequestMappingInfo(newPatternsCondition, requestMappingInfo.getMethodsCondition(),
                requestMappingInfo.getParamsCondition(), requestMappingInfo.getHeadersCondition(), requestMappingInfo.getConsumesCondition(),
                requestMappingInfo.getProducesCondition(), requestMappingInfo.getCustomCondition());
    }


    /**
     * 卸载controller
     * @param cls
     * @throws Exception
     */
    private void unregisterController(AppInfo appInfo,Class<?> cls) throws Exception {

        final RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
                applicationContext.getBean("requestMappingHandlerMapping");

        String handler = StringUtils.uncapitalize(cls.getName());
        if(!applicationContext.containsBean(handler)){
            return;
        }
        final Class<?> targetClass = cls;
        ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
            public void doWith(Method method) {
                Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                try {
                    Method createMappingMethod = RequestMappingHandlerMapping.class.
                            getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
                    createMappingMethod.setAccessible(true);
                    RequestMappingInfo requestMappingInfo = (RequestMappingInfo)
                            createMappingMethod.invoke(requestMappingHandlerMapping, specificMethod, targetClass);
                    if (requestMappingInfo != null) {
                        RequestMappingInfo newRequestMappingInfo = manipulateRequestMappingInfo(appInfo,requestMappingInfo);
                        requestMappingHandlerMapping.unregisterMapping(newRequestMappingInfo);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, ReflectionUtils.USER_DECLARED_METHODS);

        //卸载bean
        unregisterBean(cls);
    }

    /**
     * 注册bean
     * @param reclass
     * @throws Exception
     */
    private void registerBean(Class<?> reclass,AppInfo appInfo) throws Exception {


        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(reclass);
        BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();

        //设置当前bean定义对象是单利的
        beanDefinition.setScope("singleton");
        //将变量首字母置小写
        String beanName = StringUtils.uncapitalize(reclass.getName());

        //将applicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext configurableApplicationContext =
                (ConfigurableApplicationContext) applicationContext;


        // 获取bean工厂并转换为DefaultListableBeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory =
                (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();

        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);

        defaultListableBeanFactory.autowireBean(reclass);
        BeanNameMap.put(reclass.getName(),appInfo.getAppName());
    }

    public String getAppNameByClass(String className){
        return BeanNameMap.get(className);
    }

    private void addInterceptor(AppInfo appInfo,Class<?> cls){
        com.example.base.app.AppUtils.addCLS_APP(cls.getName(),appInfo.getAppName());
        com.example.base.app.AppUtils.addCLS_NAME(cls.getName(),cls);
    }


    /**
     * 卸载bean
     * @param cls
     * @throws Exception
     */
    private void unregisterBean(Class<?> cls) throws Exception {

        //将applicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext configurableApplicationContext =
                (ConfigurableApplicationContext) applicationContext;


        // 获取bean工厂并转换为DefaultListableBeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory =
                (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();

        String beanName = StringUtils.uncapitalize(cls.getName());

        if(defaultListableBeanFactory.containsBeanDefinition(beanName)){
            defaultListableBeanFactory.removeBeanDefinition(beanName);
        }

    }


//    /**
//     * 卸载controller
//     * @param cls
//     * @throws Exception
//     */
//    public void unregisterController(Class<?> cls) throws Exception {
//
//        final RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
//                applicationContext.getBean("requestMappingHandlerMapping");
//        String handler = StringUtils.uncapitalize(cls.getName());
//        if(!applicationContext.containsBean(handler)){
//            return;
//        }
//        System.out.println("handler"+handler);
//        Object controller = applicationContext.getBean(handler);
//        final Class<?> targetClass = controller.getClass();
//        ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
//            public void doWith(Method method) {
//                Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
//                try {
//                    Method createMappingMethod = RequestMappingHandlerMapping.class.
//                            getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
//                    createMappingMethod.setAccessible(true);
//                    RequestMappingInfo requestMappingInfo = (RequestMappingInfo)
//                            createMappingMethod.invoke(requestMappingHandlerMapping, specificMethod, targetClass);
//                    if (requestMappingInfo != null) {
//                        requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);
//                    }
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//            }
//        }, ReflectionUtils.USER_DECLARED_METHODS);
//
//        //卸载bean
//        unregisterBean(cls);
//    }

//
//    /**
//     * 卸载bean
//     * @param cls
//     * @throws Exception
//     */
//    public void unregisterBean(Class<?> cls) throws Exception {
//
//        //将applicationContext转换为ConfigurableApplicationContext
//        ConfigurableApplicationContext configurableApplicationContext =
//                (ConfigurableApplicationContext) applicationContext;
//
//
//        // 获取bean工厂并转换为DefaultListableBeanFactory
//        DefaultListableBeanFactory defaultListableBeanFactory =
//                (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
//
//        String beanName = StringUtils.uncapitalize(cls.getName());
//        Class<?>[] interfaces = cls.getInterfaces();
//        if(interfaces.length>0){
//            beanName=StringUtils.uncapitalize(interfaces[0].getName());
//        }
//
//        if(defaultListableBeanFactory.containsBeanDefinition(beanName)){
//            defaultListableBeanFactory.removeBeanDefinition(beanName);
//        }
//
//    }


}
